/**
 * The MOSES project, model based software engineering of networked embedded systems.
 * 
 * Copyright (C) 2010-2013, University of Applied Sciences Wildau,
 * Telematics Research Group, http://www.tm.th-wildau.de/research-group
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>.
 * 
 * Contributors:
 *     University of Applied Sciences Wildau - initial API and implementation
 * 
 * If you are interested in a commercial license please contact us via
 * e-mail: ralf.vandenhouten@th-wildau.de.
 */
package de.thwildau.tm.moses.abnf.generator;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import de.thwildau.tm.moses.abnf.abnf.Alternation;
import de.thwildau.tm.moses.abnf.abnf.BinVal;
import de.thwildau.tm.moses.abnf.abnf.CharVal;
import de.thwildau.tm.moses.abnf.abnf.Concatenation;
import de.thwildau.tm.moses.abnf.abnf.DecVal;
import de.thwildau.tm.moses.abnf.abnf.Element;
import de.thwildau.tm.moses.abnf.abnf.Group;
import de.thwildau.tm.moses.abnf.abnf.HexVal;
import de.thwildau.tm.moses.abnf.abnf.Option;
import de.thwildau.tm.moses.abnf.abnf.ProseVal;
import de.thwildau.tm.moses.abnf.abnf.Repeat;
import de.thwildau.tm.moses.abnf.abnf.Repetition;
import de.thwildau.tm.moses.abnf.abnf.Rule;
import de.thwildau.tm.moses.abnf.abnf.RuleRef;
import de.thwildau.tm.moses.abnf.abnf.RuleSet;
import de.thwildau.tm.moses.abnf.generator.JavaTemplates;
import de.thwildau.tm.moses.abnf.generator.ModelUtil;
import de.thwildau.tm.moses.abnf.generator.NameResolver;
import de.thwildau.tm.moses.abnf.generator.model.AbnfAlternation;
import de.thwildau.tm.moses.abnf.generator.model.AbnfAttribute;
import de.thwildau.tm.moses.abnf.generator.model.AbnfCharValue;
import de.thwildau.tm.moses.abnf.generator.model.AbnfCoreRule;
import de.thwildau.tm.moses.abnf.generator.model.AbnfElementField;
import de.thwildau.tm.moses.abnf.generator.model.AbnfRepeat;
import de.thwildau.tm.moses.abnf.generator.model.AbnfRuleClass;
import de.thwildau.tm.moses.abnf.generator.model.impl.AbnfAlternationImpl;
import de.thwildau.tm.moses.abnf.generator.model.impl.AbnfAttributeImpl;
import de.thwildau.tm.moses.abnf.generator.model.impl.AbnfElementFieldImpl;
import de.thwildau.tm.moses.abnf.generator.model.impl.AbnfRuleClassImpl;
import de.thwildau.tm.moses.abnf.generator.model.impl.ModelFactory;
import de.thwildau.tm.moses.xabnf.xabnf.FieldBinding;
import de.thwildau.tm.moses.xabnf.xabnf.RuleBinding;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Category;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function0;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.IteratorExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure2;
import org.eclipse.xtext.xbase.lib.StringExtensions;

/**
 * This model inferrer infers the ABNF grammar (Xtext) model into an intermediate
 * model that is used for the real code generation by the AbnfGenerator.
 */
@SuppressWarnings("all")
public class ModelInferrer {
  private final static Category LOG = new Function0<Category>() {
    public Category apply() {
      Category _instance = Logger.getInstance(ModelInferrer.class);
      return _instance;
    }
  }.apply();
  
  @Inject
  private NameResolver nameResolver;
  
  @Inject
  @Extension
  private ModelUtil modelUtil;
  
  public List<AbnfRuleClass> createModel(final Iterable<Rule> rules) {
    List<AbnfRuleClass> _xblockexpression = null;
    {
      final List<AbnfRuleClass> ruleClasses = CollectionLiterals.<AbnfRuleClass>newArrayList();
      for (final Rule r : rules) {
        {
          final AbnfRuleClassImpl ruleModel = this.createModel(r);
          String _className = ruleModel.getClassName();
          final AbnfRuleClass ele = this.find(ruleClasses, _className);
          boolean _equals = Objects.equal(ele, null);
          if (_equals) {
            ruleClasses.add(ruleModel);
          } else {
            String _className_1 = ruleModel.getClassName();
            String _plus = ("A model class of " + _className_1);
            String _plus_1 = (_plus + " already exist with a different case ");
            String _className_2 = ele.getClassName();
            String _plus_2 = (_plus_1 + _className_2);
            ModelInferrer.LOG.warn(_plus_2);
          }
        }
      }
      for (final AbnfRuleClass rc : ruleClasses) {
        {
          final AbnfRuleClassImpl abnfRuleClass = ((AbnfRuleClassImpl) rc);
          Rule _rule = abnfRuleClass.getRule();
          final AbnfRuleClass superClass = this.getSuperClass(ruleClasses, _rule);
          abnfRuleClass.setSuperClass(superClass);
        }
      }
      _xblockexpression = (ruleClasses);
    }
    return _xblockexpression;
  }
  
  private AbnfRuleClassImpl createModel(final Rule rule) {
    AbnfRuleClassImpl _xblockexpression = null;
    {
      QualifiedName _fqn = this.nameResolver.getFqn(rule);
      final String className = _fqn.getLastSegment();
      AbnfRuleClass _createAbnfRuleClass = ModelFactory.createAbnfRuleClass(rule, className);
      final AbnfRuleClassImpl model = ((AbnfRuleClassImpl) _createAbnfRuleClass);
      Alternation _alternation = rule.getAlternation();
      this.createModelFromAlternation(model, _alternation, null);
      this.normalizeAttributes(model);
      this.bindAttributeNames(model);
      _xblockexpression = (model);
    }
    return _xblockexpression;
  }
  
  /**
   * Returns the class name of the cross reference of the given rule. The returned class name
   * does not contain the package name.
   */
  private AbnfRuleClass getSuperClass(final List<AbnfRuleClass> ruleClasses, final Rule rule) {
    EObject _eContainer = rule.eContainer();
    final RuleSet ruleSet = ((RuleSet) _eContainer);
    EList<Rule> _rules = ruleSet.getRules();
    for (final Rule r : _rules) {
      boolean _equals = r.equals(rule);
      boolean _not = (!_equals);
      if (_not) {
        Alternation _alternation = r.getAlternation();
        EList<Concatenation> _concatenations = _alternation.getConcatenations();
        for (final Concatenation c : _concatenations) {
          EList<Repetition> _repetitions = c.getRepetitions();
          for (final Repetition rp : _repetitions) {
            {
              Element element = rp.getElement();
              if ((element instanceof Group)) {
                final Group group = ((Group) element);
                Alternation _alternation_1 = group.getAlternation();
                EList<Concatenation> _concatenations_1 = _alternation_1.getConcatenations();
                for (final Concatenation groupConcatenation : _concatenations_1) {
                  EList<Repetition> _repetitions_1 = groupConcatenation.getRepetitions();
                  for (final Repetition groupRp : _repetitions_1) {
                    Element _element = groupRp.getElement();
                    if ((_element instanceof RuleRef)) {
                      Element _element_1 = groupRp.getElement();
                      final RuleRef groupRuleRef = ((RuleRef) _element_1);
                      boolean _and = false;
                      Rule _rule = groupRuleRef.getRule();
                      boolean _equals_1 = _rule.equals(rule);
                      if (!_equals_1) {
                        _and = false;
                      } else {
                        Alternation _alternation_2 = group.getAlternation();
                        EList<Concatenation> _concatenations_2 = _alternation_2.getConcatenations();
                        int _size = _concatenations_2.size();
                        boolean _greaterThan = (_size > 1);
                        _and = (_equals_1 && _greaterThan);
                      }
                      if (_and) {
                        for (final AbnfRuleClass rc : ruleClasses) {
                          Rule _rule_1 = ((AbnfRuleClassImpl) rc).getRule();
                          boolean _equals_2 = _rule_1.equals(r);
                          if (_equals_2) {
                            String _name = rule.getName();
                            String _plus = ("Set super class of " + _name);
                            String _plus_1 = (_plus + " to ");
                            String _name_1 = r.getName();
                            String _plus_2 = (_plus_1 + _name_1);
                            ModelInferrer.LOG.debug(_plus_2);
                            return rc;
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    return null;
  }
  
  private AbnfRuleClass find(final List<AbnfRuleClass> ruleClasses, final String className) {
    final Function1<AbnfRuleClass,Boolean> _function = new Function1<AbnfRuleClass,Boolean>() {
        public Boolean apply(final AbnfRuleClass modelClass) {
          String _className = modelClass.getClassName();
          boolean _equalsIgnoreCase = _className.equalsIgnoreCase(className);
          return Boolean.valueOf(_equalsIgnoreCase);
        }
      };
    AbnfRuleClass _findFirst = IterableExtensions.<AbnfRuleClass>findFirst(ruleClasses, _function);
    return _findFirst;
  }
  
  /**
   * This function normalizes the AbnfAttributes in the AbnfModel. It look for Attributes
   * with the same name, counts them. Those attributes with count > 1 will get a new
   * indexed name (e.g. attName1, attName2...).
   * 
   * @param model - the Abnf model whose attributes should be normalized
   */
  public void normalizeAttributes(final AbnfRuleClassImpl model) {
    final HashMap<String,Pair<Integer,Integer>> map = CollectionLiterals.<String, Pair<Integer,Integer>>newHashMap();
    List<AbnfElementField> _elements = model.getElements();
    Iterable<AbnfAttributeImpl> attribtues = Iterables.<AbnfAttributeImpl>filter(_elements, AbnfAttributeImpl.class);
    final Function1<AbnfAttributeImpl,HashMap<String,Pair<Integer,Integer>>> _function = new Function1<AbnfAttributeImpl,HashMap<String,Pair<Integer,Integer>>>() {
        public HashMap<String,Pair<Integer,Integer>> apply(final AbnfAttributeImpl it) {
          String _name = it.getName();
          Pair<Integer,Integer> _get = map.get(_name);
          Pair<Integer,Integer> value = ((Pair<Integer,Integer>) _get);
          Integer _first = null;
          if (value!=null) {
            _first=value.getFirst();
          }
          final Integer integer = _first;
          int _xifexpression = (int) 0;
          boolean _equals = Objects.equal(integer, null);
          if (_equals) {
            _xifexpression = 0;
          } else {
            int _intValue = integer.intValue();
            _xifexpression = _intValue;
          }
          final int index = _xifexpression;
          String _name_1 = it.getName();
          int _plus = (index + 1);
          Pair<Integer,Integer> _create = Tuples.<Integer, Integer>create(Integer.valueOf(_plus), Integer.valueOf(0));
          map.put(_name_1, _create);
          return map;
        }
      };
    IterableExtensions.<HashMap<String,Pair<Integer,Integer>>, AbnfAttributeImpl>toMap(attribtues, _function);
    final Procedure2<AbnfAttributeImpl,Integer> _function_1 = new Procedure2<AbnfAttributeImpl,Integer>() {
        public void apply(final AbnfAttributeImpl ele, final Integer i) {
          String name = ele.getName();
          Pair<Integer,Integer> counterPair = map.get(name);
          Integer _first = null;
          if (counterPair!=null) {
            _first=counterPair.getFirst();
          }
          boolean _greaterThan = ((_first).intValue() > 1);
          if (_greaterThan) {
            Integer _second = counterPair.getSecond();
            int _plus = ((_second).intValue() + 1);
            String _plus_1 = (name + Integer.valueOf(_plus));
            ele.setName(_plus_1);
            Integer _first_1 = counterPair.getFirst();
            Integer _second_1 = counterPair.getSecond();
            int _plus_2 = ((_second_1).intValue() + 1);
            Pair<Integer,Integer> _create = Tuples.<Integer, Integer>create(_first_1, Integer.valueOf(_plus_2));
            map.put(name, _create);
          }
        }
      };
    IterableExtensions.<AbnfAttributeImpl>forEach(attribtues, _function_1);
  }
  
  /**
   * Binds the attributes names of the Xabnf-Binding to the attributes names of the AbnfRuleClass.
   * If no Xabnf-Binding is found for this AbnfRuleClass, this method does nothing.
   * 
   * @param model - the Abnf model whose attrbutes should be bound to specific names as specified in an Xabnf file
   */
  public void bindAttributeNames(final AbnfRuleClassImpl rc) {
    Rule _rule = rc.getRule();
    Resource _eResource = _rule.eResource();
    TreeIterator<EObject> _xabnfContent = this.nameResolver.getXabnfContent(_eResource);
    Iterator<RuleBinding> _filter = Iterators.<RuleBinding>filter(_xabnfContent, RuleBinding.class);
    final Function1<RuleBinding,Boolean> _function = new Function1<RuleBinding,Boolean>() {
        public Boolean apply(final RuleBinding rb) {
          Rule _rule = rb.getRule();
          Rule _rule_1 = rc.getRule();
          boolean _equals = _rule.equals(_rule_1);
          return Boolean.valueOf(_equals);
        }
      };
    final RuleBinding ruleBinding = IteratorExtensions.<RuleBinding>findFirst(_filter, _function);
    boolean _and = false;
    boolean _notEquals = (!Objects.equal(ruleBinding, null));
    if (!_notEquals) {
      _and = false;
    } else {
      EList<FieldBinding> _fieldBindings = ruleBinding.getFieldBindings();
      int _size = _fieldBindings.size();
      boolean _greaterThan = (_size > 0);
      _and = (_notEquals && _greaterThan);
    }
    if (_and) {
      int index = 0;
      List<AbnfElementField> _elements = rc.getElements();
      Iterable<AbnfAttributeImpl> _filter_1 = Iterables.<AbnfAttributeImpl>filter(_elements, AbnfAttributeImpl.class);
      for (final AbnfAttributeImpl field : _filter_1) {
        EList<FieldBinding> _fieldBindings_1 = ruleBinding.getFieldBindings();
        int _size_1 = _fieldBindings_1.size();
        boolean _lessThan = (index < _size_1);
        if (_lessThan) {
          EList<FieldBinding> _fieldBindings_2 = ruleBinding.getFieldBindings();
          final FieldBinding attBinding = _fieldBindings_2.get(index);
          String _className = rc.getClassName();
          String _plus = ("Bind " + _className);
          String _plus_1 = (_plus + " [");
          String _type = field.getType();
          String _plus_2 = (_plus_1 + _type);
          String _plus_3 = (_plus_2 + "] ");
          String _name = field.getName();
          String _plus_4 = (_plus_3 + _name);
          String _plus_5 = (_plus_4 + " => ");
          String _targetName = attBinding.getTargetName();
          String _plus_6 = (_plus_5 + _targetName);
          ModelInferrer.LOG.debug(_plus_6);
          String _targetName_1 = attBinding.getTargetName();
          field.setName(_targetName_1);
          int _plus_7 = (index + 1);
          index = _plus_7;
        }
      }
    }
  }
  
  public AbnfRuleClassImpl createModelFromAlternation(final AbnfRuleClassImpl model, final Alternation alternation, final AbnfRepeat outerRepeat) {
    AbnfRuleClassImpl _xblockexpression = null;
    {
      EList<Concatenation> concatenations = alternation.getConcatenations();
      int _size = concatenations.size();
      boolean _equals = (_size == 1);
      if (_equals) {
        Concatenation _get = concatenations.get(0);
        EList<Repetition> _repetitions = _get.getRepetitions();
        for (final Repetition r : _repetitions) {
          {
            Repeat _repeat = r.getRepeat();
            AbnfRepeat modelRepeat = this.createFromBaseModel(outerRepeat, _repeat);
            Element _element = r.getElement();
            this.createModel(_element, model, modelRepeat);
          }
        }
      } else {
        int _size_1 = concatenations.size();
        boolean _greaterThan = (_size_1 > 1);
        if (_greaterThan) {
          AbnfRepeat modelRepeat = this.createFromBaseModel(outerRepeat, null);
          Rule rule = ModelUtil.findRule(alternation);
          String fieldName = this.getFieldName(rule, modelRepeat, model);
          String _firstUpper = StringExtensions.toFirstUpper(fieldName);
          AbnfAlternation<? extends Object> _createAbnfAlternation = ModelFactory.createAbnfAlternation(modelRepeat, _firstUpper, fieldName);
          final AbnfAlternationImpl<AbnfElementField> alternationModel = ((AbnfAlternationImpl<AbnfElementField>) _createAbnfAlternation);
          for (final Concatenation c : concatenations) {
            EList<Repetition> _repetitions_1 = c.getRepetitions();
            final Procedure1<Repetition> _function = new Procedure1<Repetition>() {
                public void apply(final Repetition it) {
                  Element _element = it.getElement();
                  if ((_element instanceof CharVal)) {
                    Repeat _repeat = it.getRepeat();
                    AbnfRepeat _createFromBaseModel = ModelInferrer.this.createFromBaseModel(_repeat);
                    Element _element_1 = it.getElement();
                    String _value = ((CharVal) _element_1).getValue();
                    AbnfCharValue _createAbnfCharValue = ModelFactory.createAbnfCharValue(_createFromBaseModel, _value);
                    alternationModel.add(_createAbnfCharValue);
                  } else {
                    Element _element_2 = it.getElement();
                    if ((_element_2 instanceof RuleRef)) {
                      Element _element_3 = it.getElement();
                      final RuleRef ruleRef = ((RuleRef) _element_3);
                      Repeat _repeat_1 = it.getRepeat();
                      AbnfRepeat _createFromBaseModel_1 = ModelInferrer.this.createFromBaseModel(_repeat_1);
                      String _coreRuleRefOrRuleName = ModelInferrer.this.getCoreRuleRefOrRuleName(ruleRef);
                      Rule _rule = ruleRef.getRule();
                      String _name = _rule.getName();
                      AbnfAttribute _createAbnfAttribute = ModelFactory.createAbnfAttribute(_createFromBaseModel_1, _coreRuleRefOrRuleName, _name);
                      alternationModel.add(_createAbnfAttribute);
                    }
                  }
                }
              };
            IterableExtensions.<Repetition>forEach(_repetitions_1, _function);
          }
          model.add(alternationModel);
        }
      }
      _xblockexpression = (model);
    }
    return _xblockexpression;
  }
  
  private String getCoreRuleRefOrRuleName(final RuleRef ruleRef) {
    Rule _rule = ruleRef.getRule();
    final String ruleName = _rule.getName();
    final String coreRuleRef = ModelUtil.getCoreRuleReference(ruleName);
    String _xifexpression = null;
    boolean _notEquals = (!Objects.equal(coreRuleRef, null));
    if (_notEquals) {
      _xifexpression = coreRuleRef;
    } else {
      _xifexpression = ruleName;
    }
    return _xifexpression;
  }
  
  protected Object _createModel(final Group element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    Alternation _alternation = element.getAlternation();
    AbnfRuleClassImpl _createModelFromAlternation = this.createModelFromAlternation(model, _alternation, modelRepeat);
    return _createModelFromAlternation;
  }
  
  protected Object _createModel(final Option element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    Alternation _alternation = element.getAlternation();
    AbnfRepeat _createOptionalRepeat = this.createOptionalRepeat();
    AbnfRuleClassImpl _createModelFromAlternation = this.createModelFromAlternation(model, _alternation, _createOptionalRepeat);
    return _createModelFromAlternation;
  }
  
  protected Object _createModel(final RuleRef element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    boolean _xblockexpression = false;
    {
      Rule rule = element.getRule();
      String fieldName = this.getFieldName(rule, modelRepeat, model);
      boolean _xifexpression = false;
      boolean _isSimpleCoreRule = ModelUtil.isSimpleCoreRule(rule);
      if (_isSimpleCoreRule) {
        String _name = rule.getName();
        AbnfCoreRule _createAbnfCoreRule = ModelFactory.createAbnfCoreRule(modelRepeat, _name);
        boolean _add = model.add(_createAbnfCoreRule);
        _xifexpression = _add;
      } else {
        boolean _xblockexpression_1 = false;
        {
          String _name_1 = rule.getName();
          final String ruleRef = ModelUtil.getCoreRuleReference(_name_1);
          boolean _xifexpression_1 = false;
          boolean _notEquals = (!Objects.equal(ruleRef, null));
          if (_notEquals) {
            AbnfRepeat _createDefaultRepeat = this.createDefaultRepeat();
            AbnfAttribute _createAbnfAttribute = ModelFactory.createAbnfAttribute(_createDefaultRepeat, ruleRef, fieldName);
            boolean _add_1 = model.add(_createAbnfAttribute);
            _xifexpression_1 = _add_1;
          } else {
            String _name_2 = rule.getName();
            AbnfAttribute _createAbnfAttribute_1 = ModelFactory.createAbnfAttribute(modelRepeat, _name_2, fieldName);
            boolean _add_2 = model.add(_createAbnfAttribute_1);
            _xifexpression_1 = _add_2;
          }
          _xblockexpression_1 = (_xifexpression_1);
        }
        _xifexpression = _xblockexpression_1;
      }
      _xblockexpression = (_xifexpression);
    }
    return _xblockexpression;
  }
  
  protected Object _createModel(final CharVal element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    String _value = element.getValue();
    AbnfCharValue _createAbnfCharValue = ModelFactory.createAbnfCharValue(modelRepeat, _value);
    boolean _add = model.add(_createAbnfCharValue);
    return _add;
  }
  
  protected Object _createModel(final ProseVal element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    return null;
  }
  
  protected Object _createModel(final BinVal element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    return null;
  }
  
  protected Object _createModel(final DecVal element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    return null;
  }
  
  protected Object _createModel(final HexVal element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    return null;
  }
  
  public String getFieldName(final Rule rule, final AbnfRepeat abnfRepeat, final AbnfRuleClassImpl ruleElements) {
    List<AbnfElementField> list = ruleElements.getElements();
    String fieldName = "";
    int _size = list.size();
    boolean _greaterThan = (_size > 0);
    if (_greaterThan) {
      int _size_1 = list.size();
      int _minus = (_size_1 - 1);
      AbnfElementField abnfElement = list.get(_minus);
      AbnfCharValue charValue = ((AbnfElementFieldImpl) abnfElement).<AbnfCharValue>toSubtype(AbnfCharValue.class);
      String _fieldName = JavaTemplates.toFieldName(charValue);
      fieldName = _fieldName;
    }
    boolean _or = false;
    boolean _equals = Objects.equal(fieldName, null);
    if (_equals) {
      _or = true;
    } else {
      boolean _isEmpty = fieldName.isEmpty();
      _or = (_equals || _isEmpty);
    }
    if (_or) {
      String _fieldName_1 = this.toFieldName(rule);
      fieldName = _fieldName_1;
      boolean _isLoop = this.modelUtil.isLoop(abnfRepeat);
      if (_isLoop) {
        String _concat = fieldName.concat("s");
        fieldName = _concat;
      }
    }
    return fieldName;
  }
  
  public String toFieldName(final Rule rule) {
    String name = rule.getName();
    return name.toLowerCase();
  }
  
  /**
   * Creates a default AbnfRepeat object with upper/lower bound = 1
   */
  private AbnfRepeat createDefaultRepeat() {
    return ModelFactory.createAbnfRepeat(1, 1);
  }
  
  /**
   * Creates an AbnfRepeat which indicates an optional element. The
   * AbnfRepeat has lowerBound=0 and upperBound=1.
   */
  private AbnfRepeat createOptionalRepeat() {
    return ModelFactory.createAbnfRepeat(0, 1);
  }
  
  public AbnfRepeat createFromBaseModel(final AbnfRepeat outerRepeat, final Repeat innerRepeat) {
    AbnfRepeat inner = this.createFromBaseModel(innerRepeat);
    boolean _notEquals = (!Objects.equal(outerRepeat, null));
    if (_notEquals) {
      long _lowerBound = outerRepeat.getLowerBound();
      long _lowerBound_1 = inner.getLowerBound();
      long lower = (((long) _lowerBound) * ((long) _lowerBound_1));
      long _upperBound = outerRepeat.getUpperBound();
      long _upperBound_1 = inner.getUpperBound();
      long upper = (((long) _upperBound) * ((long) _upperBound_1));
      long _min = Math.min(Integer.MAX_VALUE, lower);
      lower = _min;
      long _min_1 = Math.min(Integer.MAX_VALUE, upper);
      upper = _min_1;
      return ModelFactory.createAbnfRepeat(((int) lower), ((int) upper));
    } else {
      return inner;
    }
  }
  
  public AbnfRepeat createFromBaseModel(final Repeat repeat) {
    boolean _equals = Objects.equal(repeat, null);
    if (_equals) {
      return this.createDefaultRepeat();
    }
    long _upperBound = repeat.getUpperBound();
    boolean _lessThan = (_upperBound < 1);
    if (_lessThan) {
      repeat.setUpperBound(Long.MAX_VALUE);
    }
    long _lowerBound = repeat.getLowerBound();
    long _upperBound_1 = repeat.getUpperBound();
    return ModelFactory.createAbnfRepeat(_lowerBound, _upperBound_1);
  }
  
  public Object createModel(final Element element, final AbnfRuleClassImpl model, final AbnfRepeat modelRepeat) {
    if (element instanceof BinVal) {
      return _createModel((BinVal)element, model, modelRepeat);
    } else if (element instanceof DecVal) {
      return _createModel((DecVal)element, model, modelRepeat);
    } else if (element instanceof HexVal) {
      return _createModel((HexVal)element, model, modelRepeat);
    } else if (element instanceof CharVal) {
      return _createModel((CharVal)element, model, modelRepeat);
    } else if (element instanceof Group) {
      return _createModel((Group)element, model, modelRepeat);
    } else if (element instanceof Option) {
      return _createModel((Option)element, model, modelRepeat);
    } else if (element instanceof ProseVal) {
      return _createModel((ProseVal)element, model, modelRepeat);
    } else if (element instanceof RuleRef) {
      return _createModel((RuleRef)element, model, modelRepeat);
    } else {
      throw new IllegalArgumentException("Unhandled parameter types: " +
        Arrays.<Object>asList(element, model, modelRepeat).toString());
    }
  }
}
